package cn.jtcoding.springframework.util;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Set;
import java.util.function.Function;

public final class ClassUtils {
    public static final String FILE_PROTOCOL = "file";

    /** The ".class" file suffix. */
    public static final String CLASS_FILE_SUFFIX = ".class";

    /** The package separator character: {@code '.'}. */
    private static final char PACKAGE_SEPARATOR = '.';

    private static final char INNER_CLASS_SEPARATOR = '$';

    /** The path separator character: {@code '/'}. */
    private static final char PATH_SEPARATOR = '/';

    /**
     * 提取 Class File
     */
    public static void extractClassFile(Set<File> classFileSet, File file) {
        if (!file.isDirectory()) {
            classFileSet.add(file);
            return;
        }

        File[] files = file.listFiles(tmpFile -> tmpFile.isDirectory() || tmpFile.getName().endsWith(CLASS_FILE_SUFFIX));
        if (files == null || files.length == 0) {
            return;
        }

        for (File tmpFile : files) {
            extractClassFile(classFileSet, tmpFile);
        }
    }

    public static Function<File, Class<?>> fileToClass(String packageName, ClassLoader classLoader) {
        return file -> {
            // 获取文件全路径
            String absolutePath = file.getAbsolutePath();
            // 将 '/' 替换为 '.'
            String classPath = absolutePath.replace(PATH_SEPARATOR, PACKAGE_SEPARATOR);
            // 截取包含 packageName 之后部分
            String absoluteClassName = classPath.substring(classPath.indexOf(packageName));
            // 去掉 '.class' 后缀
            String className = absoluteClassName.substring(0, absoluteClassName.lastIndexOf("."));
            try {
                return forName(className, classLoader);
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        };
    }

    public static File basePackageToFile(String basePackage, ClassLoader classLoader) {
        URL url = classLoader.getResource(convertClassNameToResourcePath(basePackage));
        if (url == null) {
            throw new IllegalArgumentException("[" + basePackage + "] package incorrect.");
        }
        if (!url.getProtocol().startsWith(FILE_PROTOCOL)) {
            throw new IllegalArgumentException("[" + basePackage + "] package incorrect.");
        }
        return new File(url.getPath());
    }

    public static Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException {
        ClassLoader clToUse = classLoader;
        if (clToUse == null) {
            clToUse = getDefaultClassLoader();
        }
        try {
            return Class.forName(name, false, clToUse);
        }
        catch (ClassNotFoundException ex) {
            int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
            if (lastDotIndex != -1) {
                String innerClassName =
                        name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
                try {
                    return Class.forName(innerClassName, false, clToUse);
                }
                catch (ClassNotFoundException ex2) {
                    // Swallow - let original exception get through
                }
            }
            throw ex;
        }
    }

    public static String resolvePackageName(Class<?> clazz) {
        Package pkg = clazz.getPackage();
        if (pkg == null) {
            return "";
        }
        return pkg.getName();
    }

    public static Class<?> resolveClassName(String className, ClassLoader classLoader)
            throws IllegalArgumentException {

        try {
            return forName(className, classLoader);
        }
        catch (IllegalAccessError err) {
            throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
                    className + "]: " + err.getMessage(), err);
        }
        catch (LinkageError err) {
            throw new IllegalArgumentException("Unresolvable class definition for class [" + className + "]", err);
        } catch (ClassNotFoundException ex) {
            throw new IllegalArgumentException("Could not find class [" + className + "]", ex);
        }
    }

    public static boolean isPresent(String className, ClassLoader classLoader) {
        try {
            forName(className, classLoader);
            return true;
        }
        catch (IllegalAccessError err) {
            throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
                    className + "]: " + err.getMessage(), err);
        }
        catch (Throwable ex) {
            // Typically ClassNotFoundException or NoClassDefFoundError...
            return false;
        }
    }

    public static String convertClassNameToResourcePath(String className) {
        if (className == null) {
            throw new IllegalArgumentException("Class name must not be null");
        }
        return className.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
    }

    public static String convertResourcePathToClassName(String className) {
        if (className == null) {
            throw new IllegalArgumentException("Class name must not be null");
        }
        return className.replace(PATH_SEPARATOR, PACKAGE_SEPARATOR);
    }

    /**
     * 类的包路径转成文件路径
     * @param clazz org.example.demo.Application
     * @return org/example/demo/Application
     */
    public static String classPackageAsResourcePath(Class<?> clazz) {
        if (clazz == null) {
            return "";
        }
        String className = clazz.getName();
        int packageEndIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
        if (packageEndIndex == -1) {
            return "";
        }
        String packageName = className.substring(0, packageEndIndex);
        return packageName.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
    }

    /**
     *  获取类加载器
     * @return 类加载器
     */
    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        }
        catch (Throwable ex) {
            // Cannot access thread context ClassLoader - falling back...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = ClassUtils.class.getClassLoader();
            if (cl == null) {
                // getClassLoader() returning null indicates the bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                }
                catch (Throwable ex) {
                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
                }
            }
        }
        return cl;
    }

    public static Object instance(Class<?> clazz) {
        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
}
